/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mathma.core.bool;

import mathma.core.expr.Expression;

import mathma.core.simp.BasicSimplifier;
import mathma.core.simp.Simplifier;
import mathma.core.simp.TargettedSimplificationAlgorithm;

import mathma.core.value.Value;
import mathma.core.value.ValueType;

import java.util.ArrayList;
import java.util.List;

/**
 * A boolean value.  This represents either true or false.  You cannot create a
 * BooleanValue object - instead, use the two stored as {@link #True} and
 * {@link #False}.
 *
 * @since 0.2.4
 * @author Ethan Levine
 */
public class BooleanValue extends Value<BooleanValue> {

    /**
     * The simplifier for booleans.  This simplifier applies DeMorgan expansions
     * where possible, un-nests nested conjunctions and nested disjunctions, and
     * cancels double negations.  This simplifier does not simplify into a
     * disjunction of conjunctions as is common.
     */
    public final static Simplifier Simplifier = new BooleanSimplifier();

    /**
     * The ValueType for BooleanValues.  This contains the simplifier and a
     * reference to this class's Class object.
     *
     * @see mathma.core.value.ValueType
     */
    public final static ValueType<BooleanValue> Type =
            new ValueType<BooleanValue>(Simplifier, BooleanValue.class);

    /**
     * The internal value of this object.
     */
    private final boolean value;

    /**
     * Logical true.
     */
    public final static BooleanValue True = new BooleanValue(true);

    /**
     * Logical false.
     */
    public final static BooleanValue False = new BooleanValue(false);

    /**
     * Creates a new BooleanValue.
     *
     * @param value the value this object will take on.
     */
    private BooleanValue(boolean value) {
        super(Type);
        this.value = value;
    }

    /**
     * Gets the primitive boolean representation of this value.
     *
     * @return a boolean representing this value.
     */
    public boolean getRawValue() {
        return value;
    }

    private static class BooleanSimplifier extends BasicSimplifier {

        private BooleanSimplifier() {
            super();
            addAlgorithms(
                    /**
                     * not (a and b) -> (not a) or (not b)
                     * not (a or b) -> (not a) and (not b)
                     */
                    new DeMorganExpansion(),
                    /**
                     * a or (b or c) -> a or b or c
                     * a and (b and c) -> a and b and c
                     * not (not a) -> a
                     */
                    new UnnestAndOrAndNotExpressions());
        }

        private class UnnestAndOrAndNotExpressions
                extends TargettedSimplificationAlgorithm {

            private UnnestAndOrAndNotExpressions() {
                super();
                // add fragments.
                mapFragment(OrExpression.class, new Fragment<BooleanValue>() {

                    @Override
                    public Expression<BooleanValue> simp(
                            Expression<BooleanValue> rawExpr) {
                        OrExpression expr = (OrExpression) rawExpr;
                        List<Expression<BooleanValue>> operands =
                                expr.getOperands();
                        List<Expression<BooleanValue>> newOperands =
                                new ArrayList<Expression<BooleanValue>>();
                        for (Expression<BooleanValue> operand : operands) {
                            if (operand.getClass().equals(OrExpression.class)) {
                                List<Expression<BooleanValue>> simpNest =
                                        ((OrExpression) simp(operand)).getOperands();
                                for (Expression<BooleanValue> simpOperand :
                                    simpNest) {
                                    newOperands.add(simpOperand);
                                }
                            } else {
                                newOperands.add(operand);
                            }
                        }
                        return new OrExpression(newOperands);
                    }
                });
                mapFragment(AndExpression.class, new Fragment<BooleanValue>() {

                    @Override
                    public Expression<BooleanValue> simp(
                            Expression<BooleanValue> rawExpr) {
                        AndExpression expr = (AndExpression) rawExpr;
                        List<Expression<BooleanValue>> operands =
                                expr.getOperands();
                        List<Expression<BooleanValue>> newOperands =
                                new ArrayList<Expression<BooleanValue>>();
                        for (Expression<BooleanValue> operand : operands) {
                            if (operand.getClass().equals(AndExpression.class)) {
                                List<Expression<BooleanValue>> simpNest =
                                        ((AndExpression) simp(operand)).getOperands();
                                for (Expression<BooleanValue> simpOperand :
                                        simpNest) {
                                    newOperands.add(simpOperand);
                                }
                            } else {
                                newOperands.add(operand);
                            }
                        }
                        return new AndExpression(newOperands);
                    }
                });
                mapFragment(NotExpression.class, new Fragment<BooleanValue>() {

                    @Override
                    public Expression<BooleanValue> simp(
                            Expression<BooleanValue> rawExpr) {
                        NotExpression expr = (NotExpression) rawExpr;
                        if (expr.getOperand().getClass().equals(
                                NotExpression.class)) {
                            return ((NotExpression) expr.getOperand()).getOperand();
                        } else {
                            return expr;
                        }
                    }
                });
            }
        }

        private class DeMorganExpansion
                extends TargettedSimplificationAlgorithm {

            private DeMorganExpansion() {
                super();
                // add fragmetns.
                mapFragment(NotExpression.class, new Fragment<BooleanValue>() {

                    @Override
                    public Expression<BooleanValue> simp(Expression<BooleanValue> rawExpr) {
                        NotExpression expr = (NotExpression) rawExpr;
                        if (expr.getOperand().getClass().equals(OrExpression.class)) {
                            // not(or(a, b)) -> and(not(a), not(b))
                            List<Expression<BooleanValue>> newOperands =
                                    new ArrayList<Expression<BooleanValue>>();
                            for (Expression<BooleanValue> oldOperand : ((OrExpression) expr.getOperand()).getOperands()) {
                                newOperands.add(new NotExpression(oldOperand));
                            }
                            return new AndExpression(newOperands);
                        } else if (expr.getOperand().getClass().equals(AndExpression.class)) {
                            // not(and(a, b)) -> or(not(a), not(b))
                            List<Expression<BooleanValue>> newOperands =
                                    new ArrayList<Expression<BooleanValue>>();
                            for (Expression<BooleanValue> oldOperand : ((AndExpression) expr.getOperand()).getOperands()) {
                                newOperands.add(new NotExpression(oldOperand));
                            }
                            return new OrExpression(newOperands);
                        } else {
                            return expr;
                        }
                    }
                });
            }
        }
    }
}
